/*
   file : shm.c
   本文件是相应对共享内存的操作的具体实现,属于最底层的管理函数集,相对应的头文件是shm.h
   上层操作可以通过调用本曾所提供的函数来对共享内存中相应的表、列、记录、索引进行操作     
   University of Jiangsu
   memoryDB      2010.6
 */

#include "shm.h"

/*
   创建并共享内存到本进程,然后初始化123区域,起始地址保存在全局指针(void *)ptr中
 */
int _get_init_shm(unsigned long size)
{
	if((shmid=shmget(IPC_PRIVATE,size,0600))<0)return -1;//-1代表创建shmm出错                   
	if((ptr=shmat(shmid,0,0))==(void *)-1)return -2;//-2代表绑定出错
	
	time(&system_start);
	last_save_time=system_start;

	ptr_db = ptr;  //ptr_db指向共享内存起始地址(即第一区域)
	ptr_tb = ptr + SIZE_DB;  //这两个指针指向共享内存第二区域起始地址
	ptr_colu = ptr + SIZE_DB + SIZE_TBS;  //这两个指针指向共享内存第三区域起始地址
	ptr_blk = ptr_blk_curr = ptr + SIZE_DB + SIZE_TBS + SIZE_COLUS;  //这两个指针指向第四区域,即数据区起始地址
	
	/*初始化第一区域DB*/
	strcpy(ptr_db->db_name,"\0");
	ptr_db->db_size=0;
	strcpy(ptr_db->pwd,"\0");

	MAX_BLOCK_NUM=(size - SIZE_ALL_ZONE)/BLOCK_SIZE;
       
	/*初始化第二区域每个TB结构*/
	int count;
	count=NUM_OF_TB;
	TB *p_temp_tb;
	p_temp_tb=ptr_tb;
	while(count--){
		p_temp_tb->id=-1;
		strcpy(p_temp_tb->tb_name,"\0");
		p_temp_tb->num_of_colu=-1;
//		p_temp_tb->curr_colu=0;
		p_temp_tb->first_block=0;
		p_temp_tb->useable_block=0;

		p_temp_tb++;
	}

	/*初始化第三区域,每个COLU结构*/
	count=NUM_OF_TB * NUM_OF_COLU;
	COLU *p_temp_colu;
	p_temp_colu=ptr_colu;
	while(count--){
		p_temp_colu->id=-1;
		strcpy(p_temp_colu->colu_name,"\0");
		p_temp_colu->length=-1;
		p_temp_colu->type=-1;

		p_temp_colu++;
	}

	/*初始化第四区域，每个块*/
	BLOCK *p_temp_blk=ptr_blk;
	count=MAX_BLOCK_NUM;
	while(count--){
		p_temp_blk->head.tb=-1;
		p_temp_blk++;
	}
	
	return 0;
}


/*写共享内存第一区域:当前打开的数据库的信息,包括数据库名称和大小
 第一区域的信息在数据库初次运行时载入，接下去不能进行修改删除,只能读
 ptr_db在shmm.h中申明,是全局指针，是DB *类型，指向第一区域的DB结构
 */
void _insert_db_info(char *db_name,unsigned long db_size,char *pwd)
{
	strcpy(ptr_db->db_name,db_name);
	ptr_db->db_size=db_size; 
	strcpy(ptr_db->pwd,pwd);
}

/*写共享内存第二区域,将一条表信息插入到第二区域中
 第二区域可以在运行时进行增改删操作:
 增:添加一个表,需要首先检查目前表的数目是否小于NUM_OF_TB,超过的话则不能增加
 改:修改表的名称
 删:删除第二区域相应的表信息,第三区域相应的列信息，第四区域相应的记录和索引
 出错返回-1,成功返回新创建的tb_id
 */
int _insert_tb_info(char *tb_name,int num_of_colu)
{
	int curr_id=0,flag=0;
	TB *p_temp_tb=ptr_tb;
	for(;curr_id!=NUM_OF_TB;curr_id++,p_temp_tb++){
		if(p_temp_tb->id == -1){
			flag=1;
			break;
		}
	}
	if(!flag)  //如果如果没有空闲空间
		return -1;
	
	p_temp_tb->id=curr_id;
	strcpy(p_temp_tb->tb_name,tb_name);
	p_temp_tb->num_of_colu=num_of_colu;
	p_temp_tb->first_block = p_temp_tb->useable_block=0;
	
	return curr_id;
}
/*写共享内存第三区域,将一条属性信息插入到第三区域中
 第三区域是相关表的属性结构信息,只能在创建数据库的时候创建其属性信息,一旦属性信息添加完,不可再增改删
 */
int _insert_colu_info(int tb_id,char *colu_name,int length,int type)
{
	COLU *p_temp_colu=ptr_colu + tb_id*NUM_OF_COLU;
	int i=0;
	int flag=0;
	for(;i!=NUM_OF_COLU;i++,p_temp_colu++){
		if(p_temp_colu->id == -1){
			flag=1;
			break;
		}
	}

	if(flag == 0)
	  return -1;

	p_temp_colu->id=i;
	strcpy(p_temp_colu->colu_name,colu_name);
	p_temp_colu->length=length;
	p_temp_colu->type=type;
	
	return 0;
}

/*
   申请一个新块,并初始化之
 */
unsigned long _require_new_block(int tb_id)
{
	int flag=0;  //标记是否找到一个完全空闲块
/*	if( (ptr_blk_curr - ptr_blk) >= (MAX_BLOCK_NUM - 1) ){  //如果ptr_blk_curr指向了最后一块了
		p_temp_blk=ptr_blk;
		while(1){
			if(p_temp_blk == ptr_blk_curr)
			  break;
			if( p_temp_blk->head.tb == -1 ){
				flag=1;
				break;
			}
			p_temp_blk++;
		}
	}
	else{  //如果ptr_blk_curr还没有指向最后一块
		flag=1;
		p_temp_blk = ++ptr_blk_curr;
	}
*/
	BLOCK *p_temp_blk,*p_temp_blk_flag;
	p_temp_blk=p_temp_blk_flag=ptr_blk_curr;
	while(1){
		if( ((void *)p_temp_blk + 2*BLOCK_SIZE) >= (ptr + ptr_db->db_size) ) //最后一块
		  p_temp_blk=ptr_blk;
		else
		  p_temp_blk++;
		
		if( ptr_blk_curr->head.tb == -1 ){
			flag=1;
			break;
		}

		if(p_temp_blk_flag == p_temp_blk)
		  break;

		ptr_blk_curr=p_temp_blk;
	}
	//以上部分,实质是试图找到一个空白块,然后让ptr_blk_curr指向其,p_temp_blk指向这个块的下一块

	if(!flag)
	  return 1;  //没有完全空闲块了
	//如果flag=1,此时,ptr_blk_curr指向找到了空闲块,p_temp_blk指向下一块

	ptr_blk_curr->head.pre=0;  //pre指向相关联的前一块的地址,此处记录的是在共享内存内的偏移量
	ptr_blk_curr->head.next=0;  //0意指这是最后一块
	ptr_blk_curr->head.tb=tb_id;  //记录本块属于哪个表
	
	TB *p_temp_tb=ptr_tb + tb_id;
	int num_of_colu=p_temp_tb->num_of_colu;  //记录本表有多少个属性
	COLU *p_temp_colu=ptr_colu + tb_id * NUM_OF_COLU;  //指向相关表的第一个属性在区域3中的起始地址
	unsigned long total_size=1;  //记录存储一条这个表的记录最大一共需要多少字节,初始为1是因为在末尾有个'\0'
	while(num_of_colu--){
		total_size += p_temp_colu->length;
		p_temp_colu++;
	}

	ptr_blk_curr->head.record_size=total_size;  
	int i;
	ptr_blk_curr->head.unused = i = (BLOCK_SIZE - sizeof(BLOCK_HEAD)) / total_size;
	ptr_blk_curr->head.useable_offset=0;  //由于是初始化一个全新的块,因此块内第一条记录即是可用记录
	int j=0;
	while(i--){  //对块内每一条记录进行初始化，每条记录的第一个字符初始化为'\0',表示这条记录为空
		ptr_blk_curr->data[j]='\0';
		j+=ptr_blk_curr->head.record_size;
	}

	unsigned long new_block=(void *)ptr_blk_curr - ptr;
	ptr_blk_curr=p_temp_blk; //让ptr_blk_curr指向刚刚分配块的下一块
	return new_block;
}


int _save_zone123_to_disk()
{
	FILE *fp;
	if((fp=_open_dict_file(ptr_db->db_name,"w+")) == NULL)
	  return -1;


	fprintf(fp,"%lu\n",ptr_db->db_size);
	fprintf(fp,"%s\n",ptr_db->pwd);
	
	int i,j;
	TB *p_temp_tb=ptr_tb;
	COLU *p_temp_colu;
	for(i=0;i!=NUM_OF_TB;i++,p_temp_tb++){
		if(p_temp_tb->id == -1)
		  continue;
		fprintf(fp,"#\n");
		fprintf(fp,"%s\n",p_temp_tb->tb_name);
		fprintf(fp,"@\n");
		for(p_temp_colu=ptr_colu + NUM_OF_COLU*i,j=0;j!=NUM_OF_COLU;j++,p_temp_colu++){
			if(p_temp_colu->id ==  -1)
			  continue;
			fprintf(fp,"%s\n%d\n%d\n",p_temp_colu->colu_name,p_temp_colu->length,p_temp_colu->type);
		}
	}
	fclose(fp);
	return 0;
}



/*
   打开磁盘上的数据字典文件,并偏移至文件开始处
 */
FILE *_open_dict_file(char *db_name,char *type)
{
	char db[100];
	strcpy(db,db_dir);  // db_files/
	strcat(db,db_name); // db_files/my_db
	if( opendir(db)==NULL){
		mkdir(db,S_IRWXU|S_IRWXG|S_IRWXO);
	}
	strcat(db,"/dict");   //获得要打开的数据库的字典文件: db_files/xxx/dict
	FILE *fp;
	if((fp=fopen(db,type))==NULL)
	  return NULL;
	fseek(fp,0,SEEK_SET);   //打开文件并且将偏移量设为文件开头0
	
	return fp;
}

/*
   给定正则表达式和字符串，判断字符串是否符合这个正则表达式
   如果符合，返回0,否则返回非0
 */
int _check_input(const char *pattern,char *input)
{
	regex_t reg;
	
	if(regcomp(&reg,pattern,REG_NOSUB | REG_EXTENDED))
	  return -1;
	
	if(!regexec(&reg,input,0,0,0)){
		regfree(&reg);
		return 0;
	}
	else{
		regfree(&reg);
		return -2;
	}
}


/*
   给定一路径，删除之
 */
void rm_files(char *name)
{
	DIR *dp;
	struct dirent *dirp;
	struct stat buf;
	lstat(name,&buf);
	char newname[256];
	
	if(S_ISDIR(buf.st_mode)){
		dp=opendir(name);
		while((dirp=readdir(dp))!=NULL){
			if(!strcmp(dirp->d_name,".") || !strcmp(dirp->d_name,".."))
			  continue;
			strcpy(newname,name);
			strcat(newname,"/");
			strcat(newname,dirp->d_name);
			rm_files(newname);
		}
		rmdir(name);
	}
	else{
		unlink(name);
	}
}

